# Vuex 的概念与基本使用
# Vuex 是什么?
Vuex 是一个专为 Vue.js 应用程序开发的全局状态管理库。它采用集中式存储管理应用的所有组件的状态,所有组件无论层级都可以获取到 Vuex 中存储的数据。
Vuex有五个核心概念:state、mutations、actions、getters 和 modules
# state
state 用于存储状态,也就是存储数据的地方。这是 Vuex 中唯一的数据源,并且每个应用中只能有唯一的一个 state 实例,这也就是为什么 Vuex 被成为集中式(中心化)的全局状态管理库。
值得注意的是,Vuex 中存储的数据是响应式的,也就是说,当 state 中的数据发生变化时,会自动驱动视图进行更新。
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
count: 0 // 响应式数据
},
...
})
# mutations
mutations 是修改 store 中数据的唯一方式(尽管我们也可以通过 $store.state.xxx 的方式直接修改,但这种操作是不合法的,强烈建议不要使用这种方式,这样会使 devTools 追踪不到数据的变化)。
mutations 中的方法接收两个参数 state 和 payload,其中 state 就是 Vuex 中的 state,而 payload 则是我们传入的额外参数,由用户自主选择。
export default new Vuex.Store({
state: {
count: 0
},
getters: {
},
mutations: {
increment(state, payload) {
state.count += payload
}
},
actions: {
},
modules: {
}
})
mutations 在形式上更像事件而不是函数,因为我们无法通过函数的形式去调用它,而是要通过:
store.commit('increment',10)
这种 commit 的方式去触发它,其中第二个参数即是我们传入的额外参数。
需要注意的是:**mutations 中是不可以书写异步方法的!**因为异步方法会让 devtools 失效。具体来说,每次执行 mutation 中的方法时,devtools 都需要记录下执行前和执行后的 Vuex 状态。但如果 mutation 中含有异步任务,因为 devtools 无法得知该异步任务的完成时间,所以不知道什么时候去记录执行后的状态(或者说是 快照)。
如果需要根据异步方法的结果改变 state,那么就需要用到 actions 了
# actions
actions 和 mutations 非常相似,但是 actions 内可以书写异步代码。
export default new Vuex.Store({
state: {
count: 0
},
getters: {
},
mutations: {
increment(state, payload) {
state.count += payload
}
},
actions: {
increment(state, payload) {
setTimeout(() => {
state.commit('increment', payload)
}, 1000);
}
},
modules: {
}
})
另外需要注意的是,我们只能通过 mutations 去修改 state 中的数据,所以,即使在 actions 中,也不可以直接修改状态,而是要通过调用 mutations 中的方法来间接操作 state 中的数据。
actions 中的方法接收两个参数,第一个参数是与 store 实例具有相同方法和属性的 context 对象,可以调用 context.commit 提交一个 mutation,或者通过 context.state 和 context.getters 来获取 state 和 getters。第二个参数为额外参数。
在项目中,我们经常用到解构的方式来简化代码:
actions: {
increment({commit}, payload) {
setTimeout(() => {
commit('increment', payload)
}, 1000);
}
},
和 mutations 类似,我们调用 actions 中的方法需要通过:
$store.dispatch('increment',10)
这种 dispatch 方式来触发,其中第二个参数即是我们传入的额外参数。
# getters
getters 其实完全可以理解为就是 Vuex 中的计算属性,官方文档中也提到了,我们可以把 getters 看做是 store 的计算属性。(剧透:其实 getters 在底层就是借助 Vue 的计算属性实现的)
getters: {
number(state) {
return state.count + 5
}
},
$store.getters.number // 访问 number
getters 可以接受两个参数,第一个参数是 state,第二个参数是其他的 getters,不常用。
getters: {
// ...
doneTodosCount: (state, getters) => {
return getters.doneTodos.length
}
}
# modules
由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。
通过 modules 可以解决上述问题,我们可以将较大的 state 分成一个个较小的 modules(模块),每个模块都拥有自己独立的 state、mutation、action、getter 和 modules,可以多次划分 modules,多层次嵌套 modules。
const moduleA = {
state: () => ({ ... }),
mutations: { ... },
actions: { ... },
getters: { ... }
}
const moduleB = {
state: () => ({ ... }),
mutations: { ... },
actions: { ... }
}
const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
}
})
store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态
需要注意的是,尽管划分出了多个模块,但这些模块最终还是要归纳到统一的顶级父模块中,并不是完全独立的模块,这点和 pinia 中不同。pinia 是去中心化的架构,没有一个顶级的模块(或者说是仓库,因为 pinia 中已经没有 modules 的概念了),pinia 中允许创建多个完全独立的小模块,而 vuex 中只能从大的模块中使用 modules 划分出小模块,小模块依然和大的模块具有从属关系,并不完全独立(并且从底层来说,所有小模块的 state 其实都是存在根模块上的)。
← 全局路由守卫 Vuex 的基本实现 →